#include "qe_log.h"
#include "qe_time.h"
#include "qe_assert.h"
#include "qe_memory.h"
#include "qe_driver.h"
#include "libfdt.h"



QELOG_DOMAIN("axi-i2c");



#define AXI_I2C_FIFO_SIZE				16
#define AXI_I2C_RESET_MASK				0x0000000A
#define AXI_I2C_GIE_ENABLE_MASK			0x80000000
#define AXI_I2C_CR_EN_MASK				0x00000001		/* Enables the AXI IIC controller */
#define AXI_I2C_CR_MSMS					0x00000004		/* Master/Slave Mode Select */
#define AXI_I2C_CR_DIR_IS_TX_MASK		0x00000008		/* Selects an AXI IIC transmit */
#define AXI_I2C_CR_NO_ACK_MASK			QE_BIT(4)		/* Transmit Acknowledge Enable */
#define AXI_I2C_CR_REPEATED_START_MASK	0x00000020		/* Repeated start */

#define AXI_I2C_INT_AS_MASK             0x00000020      /* Addressed As Slave */
#define AXI_I2C_INT_BNB_MASK			0x00000010		/* IIC Bus is Not Busy */
#define AXI_I2C_INT_RXFIFO_FULL_MASK	0x00000008		/* Receive FIFO Full */
#define AXI_I2C_INT_TXFIFO_EMPTY_MASK	0x00000004		/* Transmit FIFO Empty */
#define AXI_I2C_INT_TXFIFO_ERROR_MASK	0x00000002		/* Transmit Error */
#define AXI_I2C_INT_ARB_LOST_MASK		0x00000001		/* Arbitration Lost */

#define AXI_I2C_SR_TXFIFO_FULL			0x00000010		/* Transmit FIFO full */
#define AXI_I2C_SR_ADDR_AS_SLAVER_MASK	0x00000002		/* When addressed as slave */
#define AXI_I2C_SR_BUS_BUSY_MASK		0x00000004		/* Bus is busy */



typedef struct 
{
	volatile qe_u32 reserve1[7];
	volatile qe_u32 gie;			/* Global Interrupt Enable Register */
	volatile qe_u32 isr;			/* Interrupt Status Register */
	volatile qe_u32 reserve2;
	volatile qe_u32 ier;			/* Interrupt Enable Register */
	volatile qe_u32 reserve3[5];
	volatile qe_u32 softr;			/* Soft Reset Register */
	volatile qe_u32 reserve4[47];
	volatile qe_u32 cr;			    /* Control Register */
	volatile qe_u32 sr;			    /* Status Register */
	volatile qe_u32 txfifo;		    /* Transmit FIFO Register */
	volatile qe_u32 rxfifo;		    /* Receive FIFO Register */
	volatile qe_u32 sar;			/* Slave Address Register */
	volatile qe_u32 tfor;			/* Transmit FIFO Occupancy Register */
	volatile qe_u32 rfor;			/* Transmit FIFO Occupancy Register */
	volatile qe_u32 stbar;			/* Slave Ten Bit Address Register */
	volatile qe_u32 rfd;			/* Receive FIFO Programmable Depth Interrupt Register */
	volatile qe_u32 gpo;			/* General Purpose Output Register */
	volatile qe_u32 tsusta;		    /* Timing Parameter Register */
	volatile qe_u32 tsusto;		    /* Timing Parameter Register */
	volatile qe_u32 thdsta;		    /* Timing Parameter Register */
	volatile qe_u32 tsudat;		    /* Timing Parameter Register */
	volatile qe_u32 tbuf;			/* Timing Parameter Register */
	volatile qe_u32 thigh;			/* Timing Parameter Register */
	volatile qe_u32 tlow;			/* Timing Parameter Register */
	volatile qe_u32 thddat;		    /* Timing Parameter Register */
} axi_i2c_reg;

typedef struct 
{
    axi_i2c_reg *reg;
    qe_intc_dev *intc;
    qe_i2c_bus bus;
    qe_uint irq;
    qe_uint errors;
    qe_bool tx_complete;
    qe_bool rx_complete;
} axi_i2c_dev;

static void axi_i2c_hw_init(axi_i2c_dev *dev)
{
    /* Software reset */
    dev->reg->softr = AXI_I2C_RESET_MASK;

    /* Disable interrupt */
    dev->reg->ier = 0;
    dev->reg->gie = 0;

	dev->rx_complete = 0;
	dev->tx_complete = 0;
}

static void global_intr_enable(axi_i2c_dev *dev, qe_bool en)
{
    if (en)
		dev->reg->gie = AXI_I2C_GIE_ENABLE_MASK;
    else
		dev->reg->gie = 0x0;
}

static void i2c_enable(axi_i2c_dev *dev, qe_bool en)
{
    if (en) {
        dev->reg->cr |= AXI_I2C_CR_EN_MASK;
    } else {
        dev->reg->cr &= ~AXI_I2C_CR_EN_MASK;
    }
}

static void intr_clear_enable(axi_i2c_dev *dev, qe_u32 mask)
{
    qe_u32 ier;
   
	dev->reg->isr = mask;

    ier = dev->reg->ier;
    ier |= mask;
    dev->reg->ier = ier;
}

static void axi_i2c_isr(void *args)
{
    qe_u32 isr;
	qe_u32 clr = 0;
    axi_i2c_dev *dev = (axi_i2c_dev *)args;

    isr = dev->reg->isr & dev->reg->ier;
    
	/* Clear isr */
    dev->reg->isr = clr;

	qe_debug("isr %x", isr);

	if (isr & AXI_I2C_INT_ARB_LOST_MASK) {
		qe_error(" arb lost");
		clr = AXI_I2C_INT_ARB_LOST_MASK;
	} else if (isr & AXI_I2C_INT_TXFIFO_ERROR_MASK) {
        qe_error("isr err %x", isr);
        dev->errors++;
		clr = AXI_I2C_INT_TXFIFO_ERROR_MASK;
    } else if (isr & AXI_I2C_INT_RXFIFO_FULL_MASK) {
        dev->rx_complete = qe_true;
		clr = AXI_I2C_INT_RXFIFO_FULL_MASK;
    } else if (isr & AXI_I2C_INT_TXFIFO_EMPTY_MASK) {
        dev->tx_complete = qe_true;
		clr = AXI_I2C_INT_TXFIFO_EMPTY_MASK;
    }
}

static qe_bool is_bus_busy(axi_i2c_dev *dev)
{
	if (dev->reg->sr & AXI_I2C_SR_BUS_BUSY_MASK)
		return qe_true;
	return qe_false;
}

static qe_ret wait_bus_free(axi_i2c_dev *dev, qe_uint timeout)
{
	qe_u32 count = 0;

	while (is_bus_busy(dev)) {
		qe_usleep(1);
		if (count++ > timeout) {
			return qe_err_timeout;
		}
	}

	return qe_ok;
}

static void address7_set(axi_i2c_dev *dev, qe_u8 address, qe_bool rd)
{
    qe_u8 saddr = address<<1;
    saddr = (saddr & 0xFE) | (rd);
	dev->reg->txfifo = saddr;
}

static qe_ret axi_i2c_read(axi_i2c_dev *dev, qe_i2c_message *msg)
{
    int i = 0;
    qe_u32 cr, isr;
    qe_uint bytesize;

    /* Clear isr */
    dev->reg->isr &= AXI_I2C_INT_RXFIFO_FULL_MASK |
                     AXI_I2C_INT_TXFIFO_EMPTY_MASK |
                     AXI_I2C_INT_ARB_LOST_MASK;

    /* set rxfifo occupancy depth 1 byte */
	dev->reg->rfd = 0;

    /* if repeated start bit not set, send start bit by setting MSMS */
    cr = dev->reg->cr;
    if (!(cr & AXI_I2C_CR_REPEATED_START_MASK)) {
        address7_set(dev, msg->addr, qe_true);
        cr = AXI_I2C_CR_MSMS | AXI_I2C_CR_EN_MASK;
		if (msg->len == 1) {
			cr |= AXI_I2C_CR_NO_ACK_MASK;
		}
        dev->reg->cr = cr;
        while (!(dev->reg->sr & AXI_I2C_SR_BUS_BUSY_MASK))
            ;
        dev->reg->isr &= AXI_I2C_INT_BNB_MASK;
    } else {
        cr &= ~AXI_I2C_CR_DIR_IS_TX_MASK;
		if (msg->len == 1) {
			cr |= AXI_I2C_CR_NO_ACK_MASK;
		}
		dev->reg->cr = cr;
        address7_set(dev, msg->addr, qe_true);
    }

    /* try to receive the data from the bus */
    bytesize = msg->len;

    while (bytesize) {

        if (bytesize == 1) {
            isr = AXI_I2C_INT_ARB_LOST_MASK | 
                  AXI_I2C_INT_BNB_MASK;
        } else {
			isr = AXI_I2C_INT_ARB_LOST_MASK | 
				  AXI_I2C_INT_TXFIFO_ERROR_MASK | 
                  AXI_I2C_INT_BNB_MASK;   
        }

        while (1) {

            if (dev->reg->isr & AXI_I2C_INT_RXFIFO_FULL_MASK) {
				break;
			}

			// if (dev->reg->isr & (AXI_I2C_INT_ARB_LOST_MASK | AXI_I2C_INT_TXFIFO_ERROR_MASK | AXI_I2C_INT_BNB_MASK)) {
			// 	qe_error( "transmit err:%x %x", dev->reg->isr, dev->reg->ier);
			// 	return qe_err_param;
			// }
        }

        if (bytesize == 1) {
			if (!(msg->flags & QE_I2C_NO_STOP)) {
				dev->reg->cr = AXI_I2C_CR_EN_MASK;
			}
		}

		if (bytesize == msg->len && bytesize != 1) {
			dev->reg->cr |= AXI_I2C_CR_NO_ACK_MASK;
		}

        msg->buf[i++] = dev->reg->rxfifo;

        if ((bytesize == 1) && (msg->flags & QE_I2C_NOSTART)) {
			dev->reg->cr = AXI_I2C_CR_EN_MASK | AXI_I2C_CR_MSMS | AXI_I2C_CR_REPEATED_START_MASK;
		}

		dev->reg->isr &= AXI_I2C_INT_RXFIFO_FULL_MASK | AXI_I2C_INT_TXFIFO_ERROR_MASK |
			AXI_I2C_INT_ARB_LOST_MASK;

        bytesize--;
    }

	if (!(msg->flags & QE_I2C_NO_STOP)) {
		while (1) {
			if (dev->reg->isr & AXI_I2C_INT_BNB_MASK)
				break;
		}
	}

	if (!(dev->reg->cr & AXI_I2C_CR_REPEATED_START_MASK)) {
		dev->reg->cr = 0;
	}

	if (wait_bus_free(dev, 20000) != qe_ok) {
		qe_error("bus busy timeout");
		return qe_err_busy;
	}

    return qe_ok;
}

static qe_ret axi_i2c_int_read(axi_i2c_dev *dev, qe_i2c_message *msg)
{
    qe_u8 event;
    qe_u8 once_len;
    qe_u32 i = 0;
    qe_u32 remain = msg->len;

    global_intr_enable(dev, qe_false);
    
    /* write address to txfifo */
    address7_set(dev, msg->addr, qe_true);

    /* write RX_FIFO_PIRQ  */
    once_len = (msg->len > AXI_I2C_FIFO_SIZE) ? AXI_I2C_FIFO_SIZE : msg->len;
	dev->reg->rfd = once_len;

    /* write to CR to set MSMS = 1 and TX = 0 */
	dev->reg->cr |= AXI_I2C_CR_MSMS;
	dev->reg->cr &= ~AXI_I2C_CR_DIR_IS_TX_MASK;

    intr_clear_enable(dev, AXI_I2C_INT_RXFIFO_FULL_MASK);
    global_intr_enable(dev, qe_true);

	while (!dev->rx_complete);
	dev->rx_complete = 0; 
	qe_debug("rx done");

	dev->reg->cr |= AXI_I2C_CR_NO_ACK_MASK;

	while (remain) {
		qe_debug("remain:%d", remain);
		msg->buf[i] = dev->reg->rxfifo;
		qe_debug("rxfifo:0x%x", msg->buf[i]);
		i++;
		remain--;
	}
    
    /* write to CR to set MSMS = 0 to stop */
	dev->reg->cr &= ~AXI_I2C_CR_MSMS;

    return qe_ok;
}

static qe_ret axi_i2c_write(axi_i2c_dev *dev, qe_i2c_message *msg)
{
	int i;
	qe_u32 bytesize;
	
	qe_debug("cr:%x %x", dev->reg->cr, &dev->reg->cr);
	qe_debug("sr:%x %x", dev->reg->sr, &dev->reg->sr);
	qe_debug("isr:%x %x", dev->reg->isr, &dev->reg->isr);

	if (wait_bus_free(dev, 20000) != qe_ok) {
		qe_error("bus busy timeout");
		return qe_err_busy;
	}

	if (!(dev->reg->cr & AXI_I2C_CR_REPEATED_START_MASK)) {

		address7_set(dev, msg->addr, qe_false);

		dev->reg->isr &= AXI_I2C_INT_TXFIFO_EMPTY_MASK |
			AXI_I2C_INT_TXFIFO_ERROR_MASK |
			AXI_I2C_INT_ARB_LOST_MASK;

		dev->reg->cr = AXI_I2C_CR_MSMS | AXI_I2C_CR_DIR_IS_TX_MASK |
			AXI_I2C_CR_EN_MASK;

		while (!(dev->reg->sr & AXI_I2C_SR_BUS_BUSY_MASK)) {
		}

		dev->reg->isr &= AXI_I2C_INT_BNB_MASK;
	} else {
		address7_set(dev, msg->addr, qe_false);
	}

	i = 0;
	bytesize = msg->len;
	
	while (bytesize > 0) {

		while (1) {
			
			if (dev->reg->isr & (AXI_I2C_INT_TXFIFO_ERROR_MASK |
				AXI_I2C_INT_ARB_LOST_MASK | 
				AXI_I2C_INT_BNB_MASK)) {
				qe_error("error:0x%x", dev->reg->isr);
				return qe_err_send;
			}

			if (dev->reg->isr & AXI_I2C_INT_TXFIFO_EMPTY_MASK) {
				break;
			}
		}

		if (bytesize > 1) {
			dev->reg->txfifo = msg->buf[i++];
		} else {
			if (!(msg->flags & QE_I2C_NO_STOP)) {
				dev->reg->cr = AXI_I2C_CR_EN_MASK | AXI_I2C_CR_DIR_IS_TX_MASK;
			}
			dev->reg->txfifo = msg->buf[i++];

			if (msg->flags & QE_I2C_NOSTART) {
				dev->reg->isr &= AXI_I2C_INT_TXFIFO_ERROR_MASK;
				while (1) {
					if (dev->reg->isr & AXI_I2C_INT_TXFIFO_EMPTY_MASK)	{
						dev->reg->cr = AXI_I2C_CR_REPEATED_START_MASK | 
							AXI_I2C_CR_EN_MASK | 
							AXI_I2C_CR_DIR_IS_TX_MASK |
							AXI_I2C_CR_MSMS;
						break;
					}
				}
			}
		}

		dev->reg->isr &= AXI_I2C_INT_TXFIFO_EMPTY_MASK;
		
		bytesize--;
	}

	if (!(msg->flags & QE_I2C_NO_STOP)) {
		while (1) {
			if (dev->reg->isr & AXI_I2C_INT_BNB_MASK)
				break;
		}
	}

	if (!(dev->reg->cr & AXI_I2C_CR_REPEATED_START_MASK)) {

		if (dev->reg->cr & AXI_I2C_CR_MSMS) {
			dev->reg->cr &= ~AXI_I2C_CR_MSMS;
			while (!dev->reg->sr);
		}

		if (dev->reg->sr & AXI_I2C_SR_ADDR_AS_SLAVER_MASK) {
			dev->reg->cr = 0;
		}
	}

	return qe_ok;
}

static qe_ret axi_i2c_int_write(axi_i2c_dev *dev, qe_i2c_message *msg)
{
    int i = 0;
    qe_u8 event;
    qe_u32 remain = msg->len;

    global_intr_enable(dev, qe_false);
	dev->reg->ier = 0x0;
	dev->reg->isr = 0x0;
    i2c_enable(dev, qe_true);
    
    /* write address to txfifo */
    address7_set(dev, msg->addr, qe_false);

    /* write data to txfifo */
    while (remain && !(dev->reg->sr & AXI_I2C_SR_TXFIFO_FULL)) {
		dev->reg->txfifo = msg->buf[i];
        qe_debug("txfifo:0x%x", msg->buf[i]);
        i++;
        remain--;
    }

    /* write to CR to set MSMS = 1 and TX = 1 */
	dev->reg->cr |= AXI_I2C_CR_MSMS | AXI_I2C_CR_DIR_IS_TX_MASK;

    intr_clear_enable(dev, AXI_I2C_INT_TXFIFO_EMPTY_MASK|AXI_I2C_INT_TXFIFO_ERROR_MASK);
    global_intr_enable(dev, qe_true);
    
	while (!dev->tx_complete);
	dev->tx_complete = 0;
	qe_debug("tx done");

    //val = mb_read_reg(hw->regbase, MB_I2C_SR);
    //qelog_debug("drv", "val:0x%x remain:%d", val, remain);

    /* write to CR to set MSMS = 0 to stop */
	dev->reg->cr &= ~AXI_I2C_CR_MSMS;

    return qe_ok;
}

static qe_int axi_i2c_xfer(qe_i2c_bus *bus, qe_i2c_message *msgs, qe_uint num)
{
    int i;
	qe_ret ret;
    axi_i2c_dev *dev = (axi_i2c_dev *)bus->priv;

	qe_debug("xfer %d msgs %x", num, dev);

    for (i=0; i<num; i++) {
		qe_debug("msgs[%d] addr:%x flag:%x", i, msgs[i].addr, msgs[i].flags);
        if (msgs[i].flags & QE_I2C_RD) {
            ret = axi_i2c_read(dev, &msgs[i]);
			if (ret != qe_ok) {
				qe_error("read msg[%d] err:%d", i, ret);
				return -1;
			}
        } else {
            ret = axi_i2c_write(dev, &msgs[i]);
			if (ret != qe_ok) {
				qe_error("write msg[%d] err:%d", i, ret);
				return -1;
			}
        }
    }

    return i;
} 

static qe_i2c_bus_ops axi_i2c_ops = {
    .master_xfer = axi_i2c_xfer
};

static qe_ret axi_i2c_probe(const void *fdt, int offset)
{
    int len;
    const char *name;
    const char *name_intc;
    const qe_u32 *prop_reg;
    const qe_u32 *prop_irq;
    qe_intc_dev *intc;
    axi_i2c_dev *dev;

    name = fdt_get_name(fdt, offset, QE_NULL);

    prop_reg = (const qe_u32 *)fdt_getprop(fdt, offset, "reg", &len);
    if (!prop_reg) {
        qe_error("%s no reg prop", name);
        return qe_err_param;
    }

    name_intc = qe_of_get_interrupt_parent_name(fdt, offset);
    if (name_intc) {
        intc = (qe_intc_dev *)qe_dev_find(name_intc);
        if (!intc) {
            qe_error("%s not found", name_intc);
            return qe_err_notfind;
        }
        prop_irq = (const qe_u32 *)fdt_getprop(fdt, offset, "interrupts", &len);
        if (!prop_irq) {
            qe_error("%s no interrupts prop", name);
            return qe_err_param;
        }
    }

    dev = qe_malloc(sizeof(axi_i2c_dev));
    qe_assert(dev);

    dev->errors = 0;

    dev->reg = (axi_i2c_reg *)(qe_ubase)fdt32_to_cpu(*prop_reg);

    axi_i2c_hw_init(dev);

    if (name_intc) {
        dev->intc = intc;
        dev->irq = fdt32_to_cpu(prop_irq[1]);
        qe_intc_irq_register(dev->intc, dev->irq, axi_i2c_isr, dev);
		//qe_intc_irq_enable(dev->intc, dev->irq);
		qe_debug("register irq:%d to %s", dev->irq, name_intc);
    }

    qe_i2c_bus_register(&dev->bus, name, &axi_i2c_ops, dev);

    qe_debug("register %s %x", name, dev);

    return qe_ok;
}

static qe_device_id axi_i2c_ids[] = {
    {.compatible = "xlnx,axi-i2c"},
    {},
};

QE_DRIVER(axi_i2c) = {
    .name = "axi-i2c",
    .of_match = axi_i2c_ids,
    .probe = axi_i2c_probe,
};

QE_DRIVER_FORCE_EXPORT(axi_i2c);